﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Input;

namespace NuMvvm.Commands
{
    public class AsyncDelegateCommand<T> : ICommand
    {
        private readonly Func<T, CancellationToken, Task<bool>> _canExecuteAsync;
        private readonly Func<T, CancellationToken, Task> _executeAsync;
        private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();

        public AsyncDelegateCommand(Func<T, CancellationToken, Task> execute, Func<T, CancellationToken, Task<bool>> canExecute)
        {
            _executeAsync = execute;
            _canExecuteAsync = canExecute;
        }

        public AsyncDelegateCommand(Func<T, CancellationToken, Task> execute)
        {
            _executeAsync = execute;
            _canExecuteAsync = null;
        }
        public void RaiseCanExecuteChanged()
        {
            if (CanExecuteChanged != null)
                CanExecuteChanged(this, EventArgs.Empty);
        }

        public Task ExecuteAsync(T parameter)
        {
            return Task.Run(() => _executeAsync(parameter, _cancellationTokenSource.Token), _cancellationTokenSource.Token);
        }

        public Task<bool> CanExecuteAsync(T parameter)
        {
            if (_canExecuteAsync == null)
            {
                return Task.FromResult(true);
            }

            return Task.Run(() => _canExecuteAsync(parameter, _cancellationTokenSource.Token), _cancellationTokenSource.Token);
        }


        #region Implements ICommand 

        public event EventHandler CanExecuteChanged;

        bool ICommand.CanExecute(object parameter)
        {
            var task = CanExecuteAsync((T)parameter);
            return task.Result;
        }

        async void ICommand.Execute(object parameter)
        {
            await ExecuteAsync((T)parameter);
        }

        #endregion
    }
}
